#include "stdafx.h"
#include "File.h"
#include "Sheet.h"

namespace ExcelLibrary
{
	CFile::CFile() : m_dwRefCount(0), m_pConnection(NULL), m_pCommand(NULL), m_pRecordset(NULL)
	{
		CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);

		AddRef();
	}

	CFile::~CFile()
	{
		if (FAILED(m_pConnection->Close())) { /* Error */ }

		m_pConnection->Release();

		CoUninitialize();
	}

	HRESULT STDMETHODCALLTYPE CFile::QueryInterface(REFIID riid, void __RPC_FAR *__RPC_FAR *ppvObject)
	{
		m_dwRefCount++;
		*ppvObject = static_cast<void*>(this);
		return S_OK;
	}
    
	ULONG STDMETHODCALLTYPE CFile::AddRef(void)
	{
		m_dwRefCount++;
		return m_dwRefCount;
	}
    
	ULONG STDMETHODCALLTYPE CFile::Release(void)
	{
		m_dwRefCount--;

		DWORD dwRefCount = m_dwRefCount;

		if (dwRefCount == 0)
		{
			delete this;
		}

		return dwRefCount;
	}

	bool CFile::Open(const wchar_t* pFileName)
	{
		std::wostringstream FormatString;

		FormatString << _T("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=") << pFileName << _T(";Extended Properties=\"Excel 12.0;HDR=NO;IMEX=1\";");

		std::wstring ConnectString(FormatString.str());

		if (FAILED(CoCreateInstance(__uuidof(Connection), NULL, CLSCTX_INPROC, __uuidof(_Connection), reinterpret_cast<void**>(&m_pConnection))))
		{
			return false;
		}
		
		if (FAILED(m_pConnection->put_ConnectionString(_bstr_t(ConnectString.c_str()))))
		{
			return false;
		}

		if (FAILED(m_pConnection->Open(_bstr_t(""), _bstr_t(""), _bstr_t(""), NULL)))
		{
			return false;
		}

		if (FAILED(m_pConnection->put_CursorLocation(adUseClient)))
		{
			return false;
		}

		_Recordset* pRecordset;
		if (FAILED(m_pConnection->OpenSchema(adSchemaTables, vtMissing, vtMissing, &pRecordset)))
		{
			return false;
		}

		Fields* pFields;
		if (FAILED(pRecordset->get_Fields(&pFields)))
		{
			return false;
		}
		pFields->Release();

		VARIANT_BOOL Eof = FALSE;

		while (1)
		{
			if (FAILED(pRecordset->get_EOF(&Eof)))
			{
				return false;
			}

			if (Eof)
			{
				break;
			}

			Field* pField = NULL;
			if (FAILED(pFields->get_Item(variant_t("table_name"), &pField)))
			{
				return false;
			}
			pField->Release();

			variant_t Variant;
			if (FAILED(pField->get_Value(&Variant)))
			{
				return false;
			}

			const wchar_t* pSheetName = reinterpret_cast<const wchar_t*>(Variant.pbstrVal);
			const wchar_t* pSharp = StrStrW(pSheetName, L"$");
			if( pSharp != nullptr)
			{
				std::size_t size = lstrlen(pSharp);

				if (size <= sizeof(wchar_t))
				{
					std::wstring SheetName;
					SheetName.append(pSheetName, pSharp);

					m_SheetNames.push_back(SheetName);
				}
			}

			if (FAILED(pRecordset->MoveNext()))
			{
				return false;
			}
		}

		pRecordset->Release();

		return true;
	}

	ISheet* CFile::LoadSheet(const wchar_t* pSheetName)
	{
		CSheet* pSheet = NULL;

		std::wostringstream FormatString;

		FormatString << _T("select * from [") << pSheetName << _T("$]");

		std::wstring QueryString(FormatString.str());

		if (FAILED(CoCreateInstance(__uuidof(Command), NULL, CLSCTX_INPROC, __uuidof(_Command), reinterpret_cast<void**>(&m_pCommand))))
		{
			goto RETURN_SHEET;
		}

		if (FAILED(m_pCommand->putref_ActiveConnection(m_pConnection)))
		{
			goto RELEASE_COMMAND;
		}

		if (FAILED(m_pCommand->put_CommandText(bstr_t(QueryString.c_str()))))
		{
			goto RELEASE_COMMAND;
		}

		if (FAILED(CoCreateInstance(__uuidof(Recordset), NULL, CLSCTX_INPROC, __uuidof(_Recordset), reinterpret_cast<void**>(&m_pRecordset))))
		{
			goto RELEASE_COMMAND;
		}

		if (FAILED(m_pRecordset->putref_Source(m_pCommand)))
		{
			goto RELEASE_RECORDSET;
		}

		if (FAILED(m_pRecordset->Open(vtMissing, vtMissing, adOpenStatic, adLockReadOnly, adCmdText)))
		{
			goto RELEASE_RECORDSET;
		}

		pSheet = new CSheet(m_pRecordset);

		if (!pSheet->Initialize())
		{
			delete pSheet;
			pSheet = NULL;
		}

		m_pRecordset = NULL;

		goto RELEASE_COMMAND;

	RELEASE_RECORDSET:
		m_pRecordset->Release();

	RELEASE_COMMAND:
		m_pCommand->Release();

	RETURN_SHEET:
		return pSheet;
	}

	const wchar_t* CFile::GetSheetName(unsigned long ulSheetIdx)
	{
		return m_SheetNames[ulSheetIdx].c_str();
	}

	unsigned long CFile::GetSheetCount()
	{
		return m_SheetNames.size();
	}
}